
/*
 * REFSocket.h
 *
 *  Created on: Apr 14, 2013
 *      Author: Lucifer
 */

#ifndef	__BFX_REFSOCKET_H__
#define	__BFX_REFSOCKET_H__

#include "SocketTypes.h"
#include "SocketException.h"
#include "REFFileDescriptorSet.h"
#include "REFSocketAddress.h"

/**
 *  @namespace BFX
 *  The base namespace of our framework.
 */
namespace BFX
{

class REFSocketAddress;

/**
 * This class implements the Berkeley sockets interface.
 */
class REFSocket : public REFObject
{
	//
	// Constructions
	//
protected:
	// Called by creates a socket to accept an incoming request.
	REFSocket(SOCKET hSocket);

public:

	/**
	 * Initializes a new instance of the socket class using the specified address family, socket type and protocol.
	 * @param[in] addressFamily One of the BFX::SocketAddressFamily values.
	 * @param[in] socketType One of the BFX::SocketType values.
	 * @param[in] protocolType One of the BFX::SocketProtocolType values.
	 */
	REFSocket(SocketAddressFamily addressFamily, SocketType socketType, SocketProtocolType protocolType);

	/**
	 * Destroys the instance of the socket class.
	 */
	virtual ~REFSocket();

	//
	// Attributes
	//

	/**
	 * Returns file descriptor.
	 * @note: any external operations on it are dangerous.
	 * @return The file descriptor, or INVALID_SOCKET if not yet open or already closed.
	 */
	SOCKET GetFileDescriptor() const;

	/**
	 * Gets the address family of the socket.
	 * @return One of the BFX::SocketAddressFamily values.
	 */
	SocketAddressFamily GetAddressFamily() const;

	/**
	 * Gets the protocol type of the socket.
	 * @return One of the BFX::SocketProtocolType values.
	 */
	SocketProtocolType GetProtocolType() const;

	/**
	 * Gets the type of the Socket.
	 * @return One of the BFX::SocketType values.
	 */
	SocketType GetSocketType() const;

	/**
	 * Get the error status for the last socket operation that failed.
	 * @return The return value indicates the error code for the last operation of this socket.
	 */
	SocketError GetLastError() const;

	/**
	 * Gets a value that indicates whether a socket is connected to a remote host as of the last Send or Receive operation.
	 * @return True if the socket was connected to a remote resource as of the most recent operation; otherwise, false.
	 */
	bool IsConnected() const;

	/**
	 * Gets a value that indicates whether the Socket is bound to a specific local port.
	 * @return True if the socket is bound to a local port; otherwise, false.
	 */
	bool IsBound() const;

	/**
	 * Gets the amount of data that has been received from the network and is available to be read.
	 * @return The number of bytes of data received from the network and available to be read. Return -1 indicates an error occurred.
	 * @see BFX::REFSocket::GetLastError()
	 */
	int GetAvailable() const;

	/**
	 * Gets a value that specifies the size of the receive buffer of the socket.
	 * @return The buffer size, in bytes, of the receive buffer. The default is 8192; Specifying -1 indicates an error occurred.
	 * @see BFX::REFSocket::GetLastError()
	 */
	int GetReceiveBufferSize() const;

	/**
	 * Sets a value that specifies the size of the receive buffer of the socket.
	 * @param[in] nBufferSize The buffer size, in bytes, of the receive buffer.
	 * @see BFX::REFSocket::GetLastError()
	 */
	void SetReceiveBufferSize(int nBufferSize);

	/**
	 * Gets a value that specifies the amount of time after which a synchronous Receive call will time out.
	 * @return The receive timeout value, in milliseconds. The default value is 0, which indicates an infinite timeout period;
	 * 		Specifying -1 indicates an error occurred.
	 * @see BFX::REFSocket::GetLastError()
	 */
	int GetReceiveTimeout() const;

	/**
	 * Sets a value that specifies the amount of time after which a synchronous Receive call will time out.
	 * @param[in] nTimeout The receive timeout value, in milliseconds. Specifying 0 indicates an infinite timeout period.
	 * @see BFX::REFSocket::GetLastError()
	 */
	void SetReceiveTimeout(int nTimeout);

	/**
	 * Sets a value that specifies the size of the send buffer of the socket
	 * @return The receive timeout value, in milliseconds. The default value is 0, which indicates an infinite timeout period;
	 * 		Specifying -1 indicates an error occurred.
	 * @see BFX::REFSocket::GetLastError()
	 */
	int GetSendBufferSize() const;

	/**
	 * Sets a value that specifies the size of the send buffer of the socket
	 * @param[in] nBufferSize The size of the send buffer, in bytes. The default is 8192.
	 * @see BFX::REFSocket::GetLastError()
	 */
	void SetSendBufferSize(int nBufferSize);

	/**
	 * Gets a value that specifies the amount of time after which a synchronous Send call will time out.
	 * @return the send timeout value, in milliseconds. The default value is 0 indicates an infinite timeout period;
	 * 		Specifying -1 indicates an error occurred.
	 * @see BFX::REFSocket::GetLastError()
	 */
	int GetSendTimeout() const;

	/**
	 * Sets a value that specifies the amount of time after which a synchronous Send call will time out.
	 * @param[in] nTimeout The send timeout value, in milliseconds. Specifying 0 indicates an infinite timeout period.
	 * @see BFX::REFSocket::GetLastError()
	 */
	void SetSendTimeout(int nTimeout);

	/**
	 * Gets the local address.
	 * @return The local address that contains the local IP address and port number to which your socket is bound.
	 */
	REFSocketAddress* getLocalAddress() const
	{
		return m_pLocalAddress;
	}
	/**
	 * Gets the remote address.
	 * @return the remote address that contains the local IP address and port number to which your socket is connected.
	 */
	REFSocketAddress* getRemoteAddress() const
	{
		return m_pRemoteAddress;
	}

	//
	// Berkeley socket Implementations.
	//

	/**
	 * Creates a new Socket for a newly created connection.
	 * @return A socket for a newly created connection; otherwise NULL, indicates failed to accept new connection.
	 * @see BFX::REFSocket::GetLastError()
	 */
	REFSocket* Accept();

	/**
	 * Binds the socket to a local address.
	 * @param[in] pLocalAddress An socket address that represents the local network interfaces.
	 * @return True if the function is successful; otherwise false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool Bind(REFSocketAddress* pLocalAddress);

	/**
	 * Places a Socket in a listening state.
	 * @param[in] nBacklog The maximum length of the pending connections queue.
	 * @return true if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool Listen(int nBacklog);

	/**
	 * Establishes a connection to a remote host.
	 * @param[in] pRemoteAddress An socket address that represents the remote device.
	 * @return True if the function is successful; otherwise false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool Connect(REFSocketAddress* pRemoteAddress);

	/**
	 * Establishes a connection to a remote host.
	 * @param[in] strRemoteAddress The IP address of the remote host.
	 * @param[in] nPort The port number of the remote host.
	 * @return True if the function is successful; otherwise false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool Connect(const String& strRemoteAddress, int nPort);

	/**
	 * Determines the status of the socket.
	 * @param[in] nMicroseconds The time to wait for a response, in microseconds. Specifying -1 indicates an infinite timeout period.
	 * @param[in] mode One of the BFX::SocketPollMode values.
	 * @return The status of the socket based on the polling mode value passed in the mode parameter.\n
	 *	SocketPollMode_Read:
	 *		true if Listen has been called and a connection is pending;
	 *		true if data is available for reading;
	 *		true if the connection has been closed, reset, or terminated;
	 *		otherwise, returns false.\n
	 *	SocketPollMode_Write:
	 *		true if processing a Connect, and the connection has succeeded;
	 *		true if data can be sent;
	 *		otherwise, returns false.\n
	 *	SelectPoolMode_Error:
	 *		true if processing a Connect that does not block, and the connection has failed;
	 *		true if OutOfBandInline is not set and out-of-band data is available;
	 *		otherwise, returns false.\n
	 *	Error occurs:
	 *		false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool Poll(int nMicroseconds, SocketPollMode mode);

	/**
	 * Receives data from a bound socket into a receive buffer.
	 * @param[out] pBuffer A pointer to a buffer that is the storage location for the received data.
	 * @param[in] iOffset The position in the buffer parameter to store the received data.
	 * @param[in] nSize The number of bytes to receive.
	 * @param[in] socketFlags A bitwise combination of the BFX::SocketFlags values.
	 * @return The number of bytes received.
	 */
	int Receive( BYTE* pBuffer, int iOffset, int nSize, SocketFlags socketFlags = SocketFlags_None);

	/**
	 * Receives the specified number of bytes of data from the specified remote address.
	 * @param[out] pBuffer A pointer to a buffer that is the storage location for the received data.
	 * @param[in] iOffset The position in the buffer parameter to store the received data.
	 * @param[in] nSize The number of bytes to receive.
	 * @param[in] pRemoteAddress An socket address that represents the remote server.
	 * @return The number of bytes received.
	 */
	int ReceiveFrom( BYTE* pBuffer, int iOffset, int nSize, REFSocketAddress* pRemoteAddress);

	/**
	 * Sends the specified number of bytes of data to a connected socket.
	 * @param[in] pBuffer A pointer to a buffer that contains the data to be sent.
	 * @param[in] iOffset The position in the data buffer at which to begin sending data.
	 * @param[in] nSize The number of bytes to send.
	 * @param[in] socketFlags A bitwise combination of the BFX::SocketFlags values.
	 * @return The number of bytes send to the socket.
	 */
	int Send(const BYTE* pBuffer, int iOffset, int nSize, SocketFlags socketFlags = SocketFlags_None);

	/**
	 * Sends the specified number of bytes of data to the specified remote address.
	 * @param[in] pBuffer A pointer to a buffer that contains the data to be sent.
	 * @param[in] iOffset The position in the data buffer at which to begin sending data.
	 * @param[in] nSize The number of bytes to send.
	 * @param[in] pRemoteAddress The remote address to be send.
	 */
	int SendTo(const BYTE* pBuffer, int iOffset, int nSize, REFSocketAddress* pRemoteAddress);

	/**
	 * Gets the value that indicates whether the socket is blocking mode.
	 * @return True if the socket is blocking; otherwise false.
	 */
	bool IsBlocking() const;

	/**
	 * Sets the value that indicates whether the socket is blocking mode.
	 * @param[in] bBlocking True if the socket will block; otherwise false. the default value is true.
	 */
	void SetBlocking(bool bBlocking);

	/**
	 * Gets the specified Socket option settings.
	 * @param[in] optionLevel
	 * @param[in] optionName
	 * @param[out] bOptionValue A boolean reference value to retrieve the option settings.
	 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName,  bool& bOptionValue) const;

	/**
	 * Gets the specified Socket option settings.
	 * @param[in] optionLevel
	 * @param[in] optionName
	 * @param[out] nOptionValue A integer reference value to retrieve the option settings.
	 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName,  INT32& nOptionValue) const;

	/**
	 * Gets the specified Socket option settings.
	 * @param[in] optionLevel
	 * @param[in] optionName
	 * @param[out] pOptionValue A pointer to the buffer to retrieve the option settings.
	 * @param[in,out] pOptionValueSize A pointer to the size of the pOptionValue buffer, in bytes.
	 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName,  void* pOptionValue, INOUT int* pOptionValueSize) const;

	/**
	 * Sets the specified Socket option to the specified value.
	 * @param[in] optionLevel One of the BFX::SocketOptionLevel values.
	 * @param[in] optionName One of the BFX::SocketOptionName values.
	 * @param[in] bOptionValue The boolean value of the option.
	 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool bOptionValue);

	/**
	 * Sets the specified Socket option to the specified value.
	 * @param[in] optionLevel One of the BFX::SocketOptionLevel values.
	 * @param[in] optionName One of the BFX::SocketOptionName values.
	 * @param[in] nOptionValue The integer value of the option.
	 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, INT32 nOptionValue);

	/**
	 * Sets the specified Socket option to the specified value.
	 * @param[in] optionLevel One of the BFX::SocketOptionLevel values.
	 * @param[in] optionName One of the BFX::SocketOptionName values.
	 * @param[in] pOptionValue A pointer to the buffer in which the value for the requested option is supplied.
	 * @param[in] nOptionValueSize The size of the pOptionValue buffer in bytes.
	 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, const void* pOptionValue, int nOptionValueSize);

	/**
	 * Sets low-level operating modes for the socket using numerical control codes.
	 * @param[in] ioControlCode A command to perform on the socket.
	 * @param[in,out] pOptionValue A pointer to a parameter for command.
	 * @return True if successful, otherwise, false, and a specific error code can be retrieved by calling GetLastError.
	 */
	bool IOControl(SocketIOControlCode ioControlCode, INOUT void* pOptionValue);

	/**
	 * Disables sends and/or receives on a socket.
	 * @param[in] how One of the BFX::SocketShutdown values that specifies the operation that will no longer be allowed.
	 */
	void Shutdown(SocketShutdown how);

	/**
	 * Closes the Socket connection and releases all associated resources with a specified timeout to allow queued data to be sent.
	 * @param[in] nTimeout Wait up to timeout seconds to send any remaining data, then close the socket. The default value is -1, witch
	 *	indicates close with existing user-specified linger option. Specifying 0 indicates close abortively.
	 */
	void Close(int nTimeout = -1);

	//
	// Internal state of the socket is inherited from listener
	//
	void UpdateAcceptSocket(REFSocket* pSocket, REFSocketAddress* pRemoteAddress);

	static void StaticInitialize();

protected:
	// Updates the status of a connected socket on which a failure occurred.
	// It'll check if the connection is still open and if need to update the internal state.
	void UpdateStatusAfterError(SocketError error);

	// Attempt to set blocking mode.
	// This version will ignore failures, and will update internal block state on success.
	SocketError TrySetBlocking(bool bDesired,  bool& bCurrent);

	// Creates a accepted socket with given file descriptor and remote address.
	REFSocket* CreateAcceptSocket(SOCKET hSocket, REFSocketAddress* pRemoteAddress);

protected:
	// The socket handle.
	SOCKET	m_hSocket;

	// Both local and remote addresses.
	REF<REFSocketAddress>	m_pLocalAddress;
	REF<REFSocketAddress>	m_pRemoteAddress;

	// These are constants initialized by constructor
	SocketAddressFamily	m_addressFamily;
	SocketType			m_socketType;
	SocketProtocolType	m_protocolType;

	// The last error state.
	mutable SocketError	m_lastError;

	// The flag indicates whether the socket is connected.
	bool	m_bIsConnected;

	// When the socket is created it will be in blocking mode.
	// We'll only be able to Accept or Connect, so we only need to handle one of these cases at a time.
	bool	m_bIsBlocking;		// desired state of the socket for the user.
	bool	m_bIsRealBlocking;	// actual block state of the socket.

	bool	m_bIsListening;
};

}	//	namespace BFX

#endif	//	__BFX_REFSOCKET_H__
